#include <pthread.h>
#include <iostream>
#include <vector>
#include <unistd.h>
#include <semaphore.h>
#include "LockGuard.hpp"


using namespace std;
template <class T>
class RingQueue
{
private:
    // 对应资源占用一个
    void P(sem_t *sem)
    {
        sem_wait(sem);
    }
    // 对应资源释放一个
    void V(sem_t *sem)
    {
        sem_post(sem);
    }

public:
    RingQueue(int size = 5)
        : _queue(5), _size(size), _p_flag(0), _c_flag(0)
    {
        // 空间信号量设置为该RingQueue的大小
        sem_init(&_space_sem, 0, _size);
        // 数据信号量为0
        sem_init(&_data_sem, 0, 0);

        pthread_mutex_init(&_p_mutex, nullptr);
        pthread_mutex_init(&_c_mutex, nullptr);
    }

    ~RingQueue()
    {
        sem_destroy(&_space_sem);
        sem_destroy(&_data_sem);

        pthread_mutex_destroy(&_p_mutex);
        pthread_mutex_destroy(&_c_mutex);
    }

    void Push(const T &in)
    {
        // 要先申请信号量，减少锁竞争的次数，让线程在锁外面排队
        // 先把车票买了，再排队上车
        // 先排队买票，再上车
        P(&_space_sem);

        // 设置临界区
        {
            LockGuard guard(&_p_mutex);
            _queue[_p_flag] = in; // 覆盖式写入
            _p_flag++;
            _p_flag %= _size;
        }

        V(&_data_sem);
    }
    void Pop(T *out)
    {
        P(&_data_sem);
        
        {
            LockGuard guard(&_c_mutex);

            *out = _queue[_c_flag];
            _c_flag++;
            _c_flag %= _size;
        }
        
        V(&_space_sem);
    }

    size_t GetProducterFlag() const { return _p_flag; }
    size_t GetConsumerFlag() const { return _c_flag; }

private:
    vector<T> _queue;
    size_t _size; // 整个队列的大小，用来计算新的下标

    size_t _p_flag; // 生产者的下标
    size_t _c_flag;

    sem_t _space_sem; // 空间信号量 -- 生产者
    sem_t _data_sem;  // 数据信号量

    pthread_mutex_t _p_mutex;
    pthread_mutex_t _c_mutex;
};